#if HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */

#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif /* HAVE_SYS_TYPES_H */

#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif /* HAVE_FCNTL_H */

#if HAVE_UNISTD_H
#include <unistd.h>
#endif /* HAVE_UNISTD_H */

#if HAVE_STDLIB_H
#include <stdlib.h>
#endif /* HAVE_STDLIB_H */

#if HAVE_STDIO_H
#include <stdio.h>
#endif /* HAVE_STDIO_H */

#if HAVE_CURSES_H
#include <curses.h>
#elif HAVE_NCURSES_CURSES_H
#include <ncurses/curses.h>
#endif /* HAVE_CURSES_H */

#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif

#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif /* HAVE_SYS_SELECT_H */

#if HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif

#if HAVE_STRING_H
#include <string.h>
#endif /* HAVE_STRING_H */

#if HAVE_SYS_IOCTL_H
#include <sys/ioctl.h>
#endif /* HAVE_SYS_IOCTL_H */

#if HAVE_ERRNO_H
#include <errno.h>
#endif /* HAVE_ERRNO_H */

#ifdef HAVE_GETOPT_H
#include <getopt.h>
#endif

#if HAVE_CTYPE_H
#include <ctype.h>
#endif

#include "CTgdb.h"
#include "ITarget.h"
#include "ICustomeRequest.h"

#ifdef __cplusplus
extern "C"
{
#endif
    //#include "logger.h"
#include "interface.h"
#include "scroller.h"
#include "sources.h"
    //#include "tgdb.h"
#include "kui.h"
#include "kui_term.h"
#include "fs_util.h"
#include "cgdbrc.h"
#include "io.h"
#include "tgdb_list.h"
#include "fork_util.h"
#include "terminal.h"
#include "queue.h"
#include "rline.h"
#include "ibuf.h"
#include "usage.h"
#include "sys_util.h"
#ifdef __cplusplus
}
#endif

#ifndef SCREEN_H_
#define SCREEN_H_

#define HISTORY_FILE_NAME "readline_history.txt" 
#define GDB_MAXBUF 4096               /* GDB input buffer size */

/** 
 * Represents all the different states that can be reached while displaying
 * the tab completion information to the user.
 */
enum tab_completion_state
{
    /** Nothing has been done yet */
    TAB_COMPLETION_START,
    /** The query possibilities message was issued, and waiting for response */
    TAB_COMPLETION_QUERY_POSSIBILITIES,
    /** Displaying the tab completion */
    TAB_COMPLETION_COMPLETION_DISPLAY,
    /** Query the user to continue tab completion, and waiting for response */
    TAB_COMPLETION_QUERY_PAGER,
    /** All done */
    TAB_COMPLETION_DONE
};

/**
 * A context needed to allow the displaying of tab completion to take place. 
 * Readline calls back into CGDB with the tab completion information. CGDB can
 * not simply display all the information to the user because the displaying of
 * the tab completion data can be interactive (and is by default). So, this 
 * context keeps track of what has been displayed to the user, and what needs
 * to be displayed while CGDB get's input from the user.
 */
typedef struct tab_completion_ctx
{
    /** All of the possible completion matches, valid from [0,num_matches]. */
    char **matches;
    /** The number of completion matches in the 'matches' field. */
    int num_matches;
    /** The longest line in the 'matches' field */
    int max_length;

    /** These variables changed based on the state of this object */

    /** Total number of lines printed so far. */
    int total;
    /** Total number of lines printed so far since last pager. */
    int lines;
    /** The current tab completion state */
    enum tab_completion_state state;
} *tab_completion_ptr;


//Screen is a Singletone
class Screen
{
    public:
        ~Screen()
        {}
        static Screen* GetInstance()
        {
            return &m_instance;
        }
        int Initialize(Ctgdb* tgdb, char* debugger_path, int argc, char** argv);
        int MainLoop();
        int Run_shell_command(const char *command);
        int Tab_completion (int a, int b);
        void Send_user_command(char *line);
        int Update_kui(cgdbrc_config_option_ptr option);
        void Ridline_completion_display_func (char **matches, int num_matches, int max_length);
    private:
        Screen():m_tgdb(NULL), m_gdb_fd(-1), m_tty_fd(-1), m_last_request(NULL),
        m_kui_ctx(NULL), m_kui_map(NULL), m_kui_imap(NULL), m_kui_input_acceptable(1),
        m_is_tab_completing(0), m_current_line(NULL),
        m_rline_last_prompt(NULL), m_completion_ptr(NULL)
    {
        m_resize_pipe[0] = -1;
        m_resize_pipe[1] = -1;
    }
        static Screen m_instance;

        Ctgdb* m_tgdb;          //TGDB object
        int m_gdb_fd;		/* File descriptor for GDB communication */
        int m_tty_fd;		/* File descriptor for process being debugged */
        static char * m_readline_history_path;
        static const char *m_readline_history_filename;
        struct tgdb_request *m_last_request;
        char m_cgdb_home_dir[MAXLINE];	/* Path to home directory with trailing slash */
        /** Master/Slave PTY used to keep readline off of stdin/stdout. */
        pty_pair_ptr m_pty_pair;

        //char *m_debugger_path = NULL;	/* Path to debugger to use */

        kui_manager *m_kui_ctx;	/* The key input package */

        kui_map_set *m_kui_map;
        kui_map_set *m_kui_imap;

        /**
         * This allows CGDB to know if it is acceptable to read more input from
         * the KUI at this particular moment. Under certain circumstances, CGDB may 
         * have to complete a particular communication message to GDB, in while doing
         * so, may not want to process's the users keystrokes. This flag is purely for
         * CGDB to keep track of if it wants to read from the KUI. The KUI is always
         * ready.
         *
         * If m_kui_input_acceptable is set to 1, then input can be read from
         * the kui. Otherwise, some part of CGDB is waiting for more events to happen.
         *
         * For example, when the user types 'o' at the CGDB source window, the
         * user is requesting the file dialog to open. CGDB must first ask GDB for 
         * the list of files. While it is retreiving these files, CGDB shouldn't 
         * shoot through the rest of the kui keys available. If the user had
         * typed 'o /main' they would want to open the file dialog and start 
         * searching for a file that had the substring 'main' in it. So, CGDB must
         * first ensure all the files are retrieved, displayed in the file
         * dialog, and ensure the file dialog is ready to recieve keys from the 
         * user before the input is allowed to hit the file dialog.
         */
        int m_kui_input_acceptable;

        int m_resize_pipe[2];

        /* Readline interface */
        struct rline *m_rline;
        /**
         * If this is 1, then CGDB is performing a tab completion in the GDB window.
         * When this is true, there can be no data passed to readline. The completion
         * must be displayed to the user first.
         */
        int m_is_tab_completing;

        /* Current readline line. If the user enters 'b ma\<NL>in<NL>', where 
         * NL is the same as the user hitting the enter key, then 2 commands are
         * received by readline. The first is 'b ma\' and the second is 'in'.
         *
         * In general, the user can put a \ as the last char on the line and GDB
         * accepts this as meaning, "allow me to enter more data on the next line".
         * The trailing \, does not belong in the command to GDB, it is purely there
         * to tell GDB that the user wants to enter more data on the next line.
         *
         * For this reason, CGDB keeps this buffer. It adds the command the user
         * is typeing to this buffer. When a line comes that does not end in a \,
         * then the command is sent to GDB.
         */
        struct ibuf *m_current_line;

        /**
         * When the user is entering a multi-line command in the GDB window
         * via the \ char at the end of line, the prompt get's set to "". This
         * variable is used to remember what the previous prompt was so that it
         * can be set back when the user has finished entering the line.
         */
        char *m_rline_last_prompt;


        /** 
         * The current tab completion display context. 
         * If non-null, currently doing a display */
        tab_completion_ptr m_completion_ptr;

        /* Original terminal attributes */
        struct termios m_term_attributes;
        int Handle_request(struct tgdb_request *request);
        int User_input_loop();
        int Init_readline (void);
        int Readline_input ();
        int Child_input ();
        int Init_resize_pipe(void);
        int Resize_term(int fd);
        int User_input (void);
        void Send_key (int focus, char key);
        int Handle_tab_completion_request (tab_completion_ptr comptr, int key);
        int Get_y_or_n (int key, int for_pager);
        void Tab_completion_destroy (tab_completion_ptr comptr);
        tab_completion_ctx* Tab_completion_create (char **matches, int num_matches, int max_length);
        int Do_tab_completion (struct tgdb_list *list);
        void Rl_sigint_recved (void);
        void Rl_resize (int rows, int cols);
        void Change_prompt (const char *new_prompt);
        int Init_home_dir (void);
        int Process_gdb_commands();
        int Gdb_input ();
        void Cleanup();
        int Create_and_init_pair ();
        int Does_request_require_console_update(tgdb_request *request, int *update);
        int Add_readline_key_sequence (const char *readline_str, enum cgdb_key key);
        int Init_kui(void);
};

#endif //SCREEN_H_
